import SpawnController from '@/modules/structures/spawn'
import { RemoteHarvestTask, RemoteSourceInfo } from './types'
import { CreepRoles } from '@/modules/structures/spawn/types'
import { isInterval, isOfficialShard } from '@/ayaka'
import { getCreepWorking } from '@/utils/common'
import { goTo } from '@/modules/move'

/**
 * 计算各外矿房间需要的 carry 部件数量
 */
const getCarryPartNumber = (() => {
    // 缓存
    const cache = new Map<string, number>()

    return (roomName: string, sources: RemoteSourceInfo[]) => {
        if (cache.has(roomName)) return cache.get(roomName)!

        const remoteRoom = Game.rooms[roomName]
        const myName = global.ayaka.myName
        const reservation = remoteRoom?.controller?.reservation
        // 单个 souce 每 tick 产出的能量
        const energyPerTick = reservation && reservation.username === myName && reservation.ticksToEnd >= 300 ? 10 : 7
        // 每 t 挖到的能量 * 路径长度 * 2 / carry 容量
        const carryPartNumber = Math.ceil(
            (energyPerTick * sources.reduce((len, source) => (len = len + source.roadLength), 0) * 2) / 50,
        )
        cache.set(roomName, carryPartNumber)
        return carryPartNumber
    }
})()

/**
 * 孵化 remoteCarrier
 */
export const spawnRemoteCarrier = function (room: MyRoom, task: RemoteHarvestTask, remoteCarriers: Creep[]) {
    if (!isInterval(5) && room.energyCapacityAvailable < 400) return
    if (SpawnController.searchSpawnTaskByRole(room, CreepRoles.remoteCarrier).length) return

    const unit = [CARRY, CARRY, MOVE]
    let multiple = Math.min(Math.floor((room.energyCapacityAvailable - 100) / 150), 10)

    if (room.controller.level <= 4) multiple = Math.min(multiple, 3)
    else if (room.controller.level <= 6) multiple = Math.min(multiple, 4)

    if (!isOfficialShard() && room.controller.level <= 7 && !task.isCenterNine) multiple = Math.min(multiple, 3)

    const sourceInfos = Object.values(task.data.source)
    const carrierNumber = getCarryPartNumber(task.roomName, sourceInfos) / (2 * multiple)

    if (remoteCarriers.length >= carrierNumber) return

    // 如果 container 没有能量也不孵化
    const containerEnergy = sourceInfos.reduce((total, source) => {
        if (!source.containerId) return total

        const container = Game.getObjectById(source.containerId)
        if (!container) return total

        return total + container.store['energy']
    }, 0)
    if (containerEnergy <= 1000) return

    // 算路，如果路的建筑工地多或者寿命低就带点 work
    const work = remoteCarriers.reduce((total, creep) => {
        creep.memory.bodyParts = creep.memory.bodyParts || { [WORK]: creep.getActiveBodyparts(WORK) }
        return total + (creep.memory.bodyParts?.[WORK] || 0)
    }, 0)

    let needWork = false
    if (work < 4) {
        const remoteRoom = Game.rooms[task.roomName]
        const sites = remoteRoom.find(FIND_MY_CONSTRUCTION_SITES)
        if (sites.length > 4) {
            needWork = true
        } else {
            const roads = remoteRoom.find(FIND_STRUCTURES).filter((s) => s.structureType === STRUCTURE_ROAD)
            const roadAverageHits = roads.reduce((total, road) => total + road.hits, 0) / roads.length
            if (roadAverageHits < 3500) needWork = true
        }
    }

    const creepName = SpawnController.addSpawnTask(room, {
        role: CreepRoles.remoteCarrier,
        body: needWork
            ? [
                  [unit, multiple - 1],
                  [WORK, WORK, MOVE],
              ]
            : [unit, multiple],
    })
    if (!creepName) return
    task.remoteCarrier.push(creepName)
}

/**
 * remoteCarrier 工作
 */
export const remoteCarrierWork = function (
    myRoom: MyRoom,
    creeps: Creep[],
    task: RemoteHarvestTask,
    remoteRoom?: Room,
) {
    creeps = creeps.filter((creep) => !creep.spawning)
    if (!creeps.length) return

    // 所有存在的 container
    const containers = Object.values(task.data.source)
        .map(({ containerId }) => containerId && Game.getObjectById(containerId))
        .filter(Boolean) as StructureContainer[]

    // id -> container|tombstone 映射
    const sourceObjectMap = new Map<string, StructureContainer | Tombstone>()
    containers.forEach((container) => {
        sourceObjectMap.set(container.id, container)
    })

    // container|tombstone -> creep 映射
    const usedSourceObjectCreepMap: { [id: string]: Creep[] } = {}
    // 是否进行交头接耳，暂时仅私服进行
    const canTalk = !isOfficialShard()
    // 爬距离中心的直线距离
    const distanceToCenterMap = new Map<number, Creep[]>()
    const center = myRoom.memory.center

    creeps.forEach((creep) => {
        // 初始化内存
        if (!creep.memory.data) {
            creep.memory.data = {
                completeRooms: [creep.memory.belong],
                work: creep.getActiveBodyparts(WORK),
            } as RemoteCarrierData
        }

        // 更新状态
        getCreepWorking(creep)
        delete creep.memory.stand

        // 不带 work 的爬才参与交头接耳
        if (canTalk && !creep.memory.data.work) {
            // 预处理交头接耳数据
            if (!creep.memory.talked) {
                creep.memory.talked = {}
            }

            for (const creepName of Object.keys(creep.memory.talked)) {
                creep.memory.talked[creepName] -= 1
                if (creep.memory.talked[creepName] <= 0) {
                    delete creep.memory.talked[creepName]
                }
            }

            // 计算距离中心的直线距离
            const distance = creep.pos.getRangeTo(center[0], center[1])
            const creepsInMap = distanceToCenterMap.get(distance) || []
            if (creepsInMap.length) {
                creepsInMap.push(creep)
            } else {
                distanceToCenterMap.set(distance, [creep])
            }
        }

        const sourceId = creep.memory.sourceId
        if (!sourceId) return
        usedSourceObjectCreepMap[sourceId] = usedSourceObjectCreepMap[sourceId] || []
        usedSourceObjectCreepMap[sourceId].push(creep)
    })

    // 交头接耳
    if (canTalk) {
        creeps.forEach((creep) => {
            if (!creep.memory.talked || creep._talked) return

            const distance = creep.pos.getRangeTo(center[0], center[1])
            const nearCreeps = [
                ...(distanceToCenterMap.get(distance - 1) || []),
                ...(distanceToCenterMap.get(distance) || []),
                ...(distanceToCenterMap.get(distance + 1) || []),
            ]

            for (const nearCreep of nearCreeps) {
                // 工作状态不匹配
                if (creep.memory.working === nearCreep.memory.working) continue
                // 不相邻或者无法交谈
                if (!creep.pos.isNearTo(nearCreep) || !nearCreep.memory.talked) continue
                // 容量不匹配
                if (creep.store.getUsedCapacity() !== nearCreep.store.getFreeCapacity()) continue
                // 已经交谈过了
                if (creep.memory.talked[nearCreep.name]) continue
                // 交谈
                if (creep.memory.working) {
                    creep.transfer(nearCreep, RESOURCE_ENERGY)
                    nearCreep.memory.targetId = creep.memory.targetId
                } else {
                    nearCreep.transfer(creep, RESOURCE_ENERGY)
                    creep.memory.targetId = nearCreep.memory.targetId
                }

                creep.memory.working = !creep.memory.working
                nearCreep.memory.working = !nearCreep.memory.working
                creep._talked = true
                nearCreep._talked = true
                creep.memory.talked[nearCreep.name] = 8
                nearCreep.memory.talked[creep.name] = 8
                break
            }
        })
    }

    creeps.forEach((creep) => {
        if (creep.store['energy'] <= 0 && creep.ticksToLive! <= 80) {
            creep.suicide()
            return
        }

        // 中央九房特殊处理
        if (task.isCenterNine && !task.isCenter && remoteRoom?.name === creep.room.name) {
            if (remoteRoom.enemys?.length) {
                // 离最近的敌人5格远
                const enemy = creep.pos.findClosestByRange(remoteRoom.enemys)
                if (enemy) {
                    const range = creep.pos.getRangeTo(enemy)
                    if (range < 5) {
                        goTo(creep, enemy.pos, { range: 5, flee: true })
                        return
                    } else if (range === 5) return
                }
            }
        }

        if (creep.memory.slack) return

        const data = creep.memory.data as RemoteCarrierData

        if (creep.memory.working) {
            delete creep.memory.sourceId

            // 先造路
            if (!data.completeRooms.includes(creep.room.name) && data.work) {
                if (!data.targetSiteId) {
                    const sites = creep.room.find(FIND_CONSTRUCTION_SITES).filter((s) => s.my)
                    if (sites.length) {
                        data.targetSiteId = creep.pos.findClosestByRange(sites)?.id || sites[0].id
                    } else {
                        data.completeRooms.push(creep.room.name)
                    }
                    return
                }

                const site = Game.getObjectById(data.targetSiteId)
                if (!site) {
                    delete data.targetSiteId
                    return
                }

                if (creep.pos.getRangeTo(site) <= 3) {
                    creep.build(site)
                    creep.memory.stand = true
                } else {
                    goTo(creep, site.pos, { range: 1 })
                }
            } else {
                if (!creep.memory.targetId) {
                    if (myRoom.storage && myRoom.storage.store.getFreeCapacity() > 50000) {
                        creep.memory.targetId = myRoom.storage.id
                    } else if (myRoom.terminal && myRoom.terminal.store.getFreeCapacity() > 50000) {
                        creep.memory.targetId = myRoom.terminal.id
                    } else {
                        if (!myRoom.container.length) {
                            creep.memory.slack = 21
                            return
                        }
                        // 找空闲空间最多的 container
                        // const centerPos = new RoomPosition(
                        //     myRoom.memory.center?.[0] || 25,
                        //     myRoom.memory.center?.[0] || 25,
                        //     myRoom.name,
                        // )
                        const container = _.max(
                            myRoom.container,
                            (c) => c.store.getFreeCapacity() - myRoom.controller.pos.getRangeTo(c) * 30,
                        )
                        if (container) {
                            creep.memory.targetId = container.id
                        } else {
                            creep.memory.slack = 21
                            return
                        }
                    }
                }

                const target = Game.getObjectById(creep.memory.targetId as Id<AnyStoreStructure>)

                if (!target || target.store.getFreeCapacity('energy') <= 0) {
                    delete creep.memory.targetId
                    return
                }

                if (!creep.pos.isNearTo(target)) {
                    goTo(creep, target.pos)
                    if (data.work) {
                        if (!data.roadId) {
                            if (isInterval(3)) {
                                const road = creep.pos
                                    .findInRange(FIND_STRUCTURES, 3)
                                    .find((s) => s.structureType === STRUCTURE_ROAD && s.hits < 4500) as StructureRoad
                                if (road) {
                                    data.roadId = road.id
                                    creep.repair(road)
                                }
                            }
                        } else {
                            const road = Game.getObjectById(data.roadId)
                            if (road && road.hits < 4500 && creep.pos.getRangeTo(road) <= 3) {
                                creep.repair(road)
                            } else {
                                delete data.roadId
                            }
                        }
                    }
                } else {
                    creep.transfer(target, 'energy')
                }
            }
        } else {
            delete creep.memory.targetId
            delete data.targetSiteId

            if (!creep.memory.sourceId) {
                // 找资源最多的 container
                const container = containers
                    .filter((c) => {
                        if (c._freeEnergy === undefined) {
                            c._freeEnergy =
                                c.store.getUsedCapacity() -
                                (usedSourceObjectCreepMap[c.id] || []).reduce(
                                    (total, creep) => total + creep.store.getFreeCapacity(),
                                    0,
                                )
                        }
                        return c._freeEnergy > 0
                    })
                    .sort((a, b) => b._freeEnergy! - a._freeEnergy!)[0]
                if (container) {
                    creep.memory.sourceId = container.id
                    if (!usedSourceObjectCreepMap[container.id]) {
                        usedSourceObjectCreepMap[container.id] = []
                    }
                    usedSourceObjectCreepMap[container.id].push(creep)
                }
            }

            // 再找尸体
            if (!creep.memory.sourceId && remoteRoom && task.isCenterNine && !task.isCenter) {
                const tombstones = remoteRoom
                    .find(FIND_TOMBSTONES, {
                        filter: (t) =>
                            t.store.getUsedCapacity() >= 300 && !sourceObjectMap.get(t.id) && t.ticksToDecay >= 50,
                    })
                    .sort(
                        (a, b) => a.ticksToDecay + creep.pos.getRangeTo(a) - (b.ticksToDecay + creep.pos.getRangeTo(b)),
                    )

                if (tombstones.length) {
                    creep.memory.sourceId = tombstones[0].id
                    sourceObjectMap.set(tombstones[0].id, tombstones[0])
                } else {
                    creep.memory.slack = 11
                    return
                }
            }

            if (!creep.memory.sourceId) return

            const sourceObject = sourceObjectMap.get(creep.memory.sourceId)
            if (!sourceObject || !sourceObject.store.getUsedCapacity()) {
                delete creep.memory.sourceId
                return
            }

            // if (container.store.getUsedCapacity() < Math.min(creep.store.getFreeCapacity(), 800)) {
            //     if (creep.pos.getRangeTo(container) > 3) {
            //         goTo(creep, container.pos, { range: 3 })
            //     } else {
            //         creep.memory.slack = 7
            //     }
            //     return
            // }

            if (creep.pos.isNearTo(sourceObject)) {
                creep.withdraw(sourceObject, Object.keys(sourceObject.store)[0] as ResourceConstant)
            } else {
                goTo(creep, sourceObject.pos, { range: 1 })
            }
        }
    })
}

interface RemoteCarrierData {
    /**
     * 房间内建筑工地是否完全造完
     */
    completeRooms: string[]
    /**
     * work 部件数量
     */
    work: number
    /**
     * 目标建筑工地 id
     */
    targetSiteId?: Id<ConstructionSite>
    /**
     * 路的 id
     */
    roadId?: Id<StructureRoad>
}

declare global {
    interface StructureContainer {
        /**
         * 未被预定的能量数
         */
        _freeEnergy?: number
    }
}
